package com.gmrz.uaf.remote.protocol.v1.processor;

import com.gmrz.service.challengestore.ChallengeStoreException;
import com.gmrz.uaf.common.Constants;
import com.gmrz.uaf.common.GuiceUtil;
import com.gmrz.uaf.common.UAFException;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.db.UAFDAOFactory;
import com.gmrz.uaf.db.UAFDBConnectionMgr;
import com.gmrz.uaf.db.dao.AuthenticatorDAO;
import com.gmrz.uaf.policy.verification.StepupPolicyProcessor;
import com.gmrz.uaf.protocol.v1.json.UAFSchemaBuilder;
import com.gmrz.uaf.protocol.v1.processor.UAFBaseProcessor;
import com.gmrz.uaf.protocol.v1.processor.exception.AuthenticationFailedException;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.uaf.protocol.v1.schema.*;
import com.gmrz.uaf.remote.plugin.AuthServicePluginManager;
import com.gmrz.uas.plugin.authservice.AuthServiceData;
import com.gmrz.uas.plugin.authservice.AuthServicePlugin;
import com.gmrz.uas.plugin.exception.PluginException;
import com.gmrz.util.Convert;
import com.gmrz.util.CryUtil;
import com.gmrz.util.UUIDGenerator;
import com.gmrz.util.db.DBUtil;
import com.google.gson.Gson;
import com.google.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

public class UAPOOBAuthInitProcessor extends UAFBaseProcessor {
    private static final Logger LOG = LogManager.getLogger(UAPOOBAuthInitProcessor.class);

    AuthServicePluginManager serviceManager;

    @Inject
    public void setServiceManager(AuthServicePluginManager serviceManager) {
        this.serviceManager = serviceManager;
    }

    /**
     *
     * @param username
     * @param appID
     * @param authTypes
     * @param transType
     * @param deviceID
     * @param authToken
     * @return
     * @throws UAFException
     * @throws ChallengeStoreException
     */
    public Map<String, AuthenticationRequest> request(String username, String appID, List<String> authTypes, String transType, String deviceID, String authToken,String authData,String custNo,String transactionText,byte[] challengeParam , String ext ) throws UAFException, ChallengeStoreException, PluginException, SQLException {
        // 根据口令从缓存中获取二维码数据
        OOBInfo oobInfo = this.getCacheOOBInfo(authToken);
        if (null == oobInfo) {
            throw new UAFException(UAFErrorCode.PROTOCOL_AUTHTOKEN_NOT_FOUND);
        }
        if(oobInfo.getStatus() != Constants.OOBStatus.NONE_SCANCODE.getStatus()){
            throw new UAFException(UAFErrorCode.PROTOCOL_AUTHTOKEN_STATUS_ERROR);
        }
        oobInfo.setStatus(Constants.OOBStatus.SCANCODE.getStatus());
        oobInfo.setAuthData(authData);
        this.cacheOOBInfo(oobInfo, authToken);
        Connection conn = UAFDBConnectionMgr.getConnection();
        try {
            AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
            int fromStatus = Constants.OOBStatus.NONE_SCANCODE.getStatus();
            int toStatus = Constants.OOBStatus.SCANCODE.getStatus();
            adao.updateOOBCodeStatus(authToken,fromStatus,toStatus);
        }catch (DAOException e1){
            LOG.error("insert oob code error",e1);
        }finally {
            DBUtil.close(conn);
        }
        Map<String, AuthenticationRequest> requests = new HashMap<String, AuthenticationRequest>();
        Gson gson = UAFSchemaBuilder.getGson();
        for (String authType : authTypes) {
            AuthenticationRequest request = null;
            if(authType.compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) < 0) {

                Policy p = this.policyManager.getNamedPolicy(authType, appID, transType);
                LOG.info("Policy:" + UAFSchemaBuilder.getGson().toJson(p));
                request = new AuthenticationRequest();
                if (p != null) {
                    if (authType.compareTo(Constants.AuthType.REMOTE_FACEPRINT.getAuthType()) >= 0 && authType.compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) < 0) {
                        MatchCriteria acceptedMatchCriteria = getAcceptedMatchCriteria(p);
                        if (!authType.equals(Constants.AuthType.REMOTE_OOBAUTH.getAuthType())) {
                            Set<Authenticator> registeredList = null;
                            if (authType.compareTo(Constants.AuthType.REMOTE_FACEPRINT.getAuthType()) >= 0 && authType.compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) < 0) {

                                registeredList = getAuthenticatorListForAlreadyRegisteredNoDeviceId(username, appID, authType, transType);
                            } else {
                                registeredList = getAuthenticatorListForAlreadyRegistered(username, appID, deviceID,
                                        authType, transType);
                            }
                            if ((registeredList == null) || (registeredList.isEmpty())) {
                                p.setAccepted(null);
                            } else {
                                StepupPolicyProcessor.filterPolicy(p, registeredList);
                                acceptedMatchCriteria = getAcceptedMatchCriteria(p);
                                String pluginUserName = CryUtil.getSHA(username + "_" + appID + "_" + authType + "_" + transType);
                                LOG.debug("pluginusername=" + username + "_" + appID + "_" + authType + "_" + transType);
                                initAuthenticatorExts(acceptedMatchCriteria, authType, pluginUserName);
                            }
                        }
                    } else {
                        Set<Authenticator> registeredList = null;
                        registeredList = getAuthenticatorListForAlreadyRegistered(username, appID, deviceID,
                                authType, transType);

                        if ((registeredList == null) || (registeredList.isEmpty())) {
                            p.setAccepted(null);
                        } else {
                            StepupPolicyProcessor.filterPolicy(p, registeredList);
                        }
                    }
                    request = GuiceUtil.getProcessorInjector().getInstance(AuthenticationRequest.class);
                    byte[] challenge = getRandom(32);
                    request.getOperationHeader().getServerData().withChallenge(challenge)
                            .withPolicyName(p.getPolicyName()).withUserName(username);
                    request.getOperationHeader().setApplicationID(this.getServerConfig().getApplicationID(appID, transType));
                    request.setServerChallenge(new ServerChallenge());
                    request.getServerChallenge().setChallenge(challenge);
                    request.setPolicy(p);
                    //request.setUsername(username);
                    if (serverConfig.getAppIdUrlBoolean(appID, transType)) {
                        setFacetsAaid(request, appID);
                    }
                }
            }else {
                UAPCertAuthInitProcessor processor = GuiceUtil.getProcessorInjector().getInstance(UAPCertAuthInitProcessor.class);
                List<String> authTypeList = new ArrayList<String>();
                authTypeList.add(authType);
                List<AuthenticationRequest> list = processor.request( custNo,  username,  appID,  deviceID, authTypeList,
                         transType,  transactionText,  challengeParam,  ext);
                if(null != list && list.size() > 0){
                    request = list.get(0);
                }
            }
            requests.put(authType, request);
        }
        return requests;

    }
    /**
     * 获取远程认证数据，例如：声纹数字
     * @param criteria
     * @param authType
     * @param username
     */
    public void initAuthenticatorExts(MatchCriteria criteria, String authType, String username) {
        try{
            String aaid = criteria.getAAID().get(0).toString();
            AuthServicePlugin service = serviceManager.getServicePlugin(aaid, authType);
            List<AuthServiceData> contents = service.preauthenticate(username);
            if(contents != null && contents.size() > 0){
                List<Extension> exts = new ArrayList<Extension>();
                for(AuthServiceData content: contents){
                    Extension ext = new Extension();
                    ext.setId(content.getId());
                    ext.setData(Convert.toBase64(content.getContent()));
                    exts.add(ext);
                }
                criteria.setExtensions(exts);
            }
        }catch (PluginException e){
            LOG.error(e.getErrorMsg());
            throw new AuthenticationFailedException(e.getErrorMsg());
        }

    }

}
